﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DeepTime.Controls
{

    //TODO
    // Add scroll direction setting
    // Do scroll logic
    [TemplatePart(Name="PART_YearsItems", Type=typeof(ItemsControl))]
    [TemplatePart(Name="PART_MonthsItems", Type=typeof(ItemsControl))]
    [TemplatePart(Name="PART_DaysItems", Type=typeof(ItemsControl))]
    [TemplatePart(Name = "PART_YearsViewPort", Type = typeof(Canvas))]
    [TemplatePart(Name = "PART_MonthsViewPort", Type = typeof(Grid))]
    [TemplatePart(Name = "PART_DaysViewPort", Type = typeof(Grid))]
    //[TemplateVisualState]
    public class FlowDateSelector : Control
    {
        #region Class Members

        private string _YearFormat = "yyyy";
        private string _MonthFormat = "MM/yyyy";
        private string _DayFormat = "dd/MM/yyyy";

        private bool _MousePressedOnYears = false;
        private bool _MousePressedOnMonths = false;
        private bool _MousePressedOnDays = false;

        private ItemsControl _YearItemsPart;
        private ItemsControl _MonthItemsPart;
        private ItemsControl _DayItemsPart;

        private Canvas _YearViewPortPart;
        private Canvas _MonthViewPortPart;
        private Canvas _DayViewPortPart;

        private Dictionary<string, DateIncrement> _Years;
        private Dictionary<string, DateIncrement> _Months;
        private Dictionary<string, DateIncrement> _Days;

        private Point _LastPoint = new Point();

        #endregion Class Members

        #region Constructor

        public FlowDateSelector()
            : this(DateTime.Now.Year - 5, DateTime.Now.Year + 5, DateTime.Now)
        {
        }
        
        public FlowDateSelector(int startYear, int endYear, DateTime currentDate)
        {
            DefaultStyleKey = typeof(FlowDateSelector);

            //
            // Set Defaults
            //
            StartYear = startYear;
            EndYear = endYear;
            CurrentDate = currentDate;

            //
            // Subscribe to control events
            //
            this.LostMouseCapture += new MouseEventHandler(FlowDateSelector_LostMouseCapture);
        }

        #endregion Constructor

        #region Dependancy Properties

        #region StartYear

        public int StartYear
        {
            get { return (int)GetValue(StartYearProperty); }
            set { SetValue(StartYearProperty, value); }
        }

        // Using a DependencyProperty as the backing store for StartYear.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty StartYearProperty =
            DependencyProperty.Register("StartYear", typeof(int), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToInt32(0), new PropertyChangedCallback(StartYearChanged)));

        private static void StartYearChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null) return;
            fds.PopulateDateIncrements();

            fds.InvalidateMeasure();
            fds.InvalidateArrange();
        }

        #endregion StartYear

        #region EndYear

        public int EndYear
        {
            get { return (int)GetValue(EndYearProperty); }
            set { SetValue(EndYearProperty, value); }
        }

        // Using a DependencyProperty as the backing store for EndYear.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty EndYearProperty =
            DependencyProperty.Register("EndYear", typeof(int), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToInt32(0), new PropertyChangedCallback(EndYearChanged)));

        private static void EndYearChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null) return;
            fds.PopulateDateIncrements();

            fds.InvalidateMeasure();
            fds.InvalidateArrange();
        }

        #endregion EndYear

        #region CurrentDate

        public DateTime CurrentDate
        {
            get { return (DateTime)GetValue(CurrentDateProperty); }
            set { SetValue(CurrentDateProperty, value); }
        }

        // Using a DependencyProperty as the backing store for CurrentDate.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty CurrentDateProperty =
            DependencyProperty.Register("CurrentDate", typeof(DateTime), typeof(FlowDateSelector), new PropertyMetadata(new PropertyChangedCallback(CurrentDateChanged)));

        private static void CurrentDateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null) return;
            fds.SetYearOffsetFromCurrentDate();
            fds.SetMonthOffsetFromCurrentDate();
            fds.SetDayOffsetFromCurrentDate();
        }

        #endregion CurrentDate

        #region YearExtentHeight

        public double YearExtentHeight
        {
            get { return (double)GetValue(YearExtentHeightProperty); }
            private set { SetValue(YearExtentHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for YearExtentHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YearExtentHeightProperty =
            DependencyProperty.Register("YearExtentHeight", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion YearExtentHeight

        #region YearExtentWidth

        public double YearExtentWidth
        {
            get { return (double)GetValue(YearExtentWidthProperty); }
            private set { SetValue(YearExtentWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for YearExtentWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YearExtentWidthProperty =
            DependencyProperty.Register("YearExtentWidth", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion YearExtentWidth

        #region MonthExtentHeight

        public double MonthExtentHeight
        {
            get { return (double)GetValue(MonthExtentHeightProperty); }
            private set { SetValue(MonthExtentHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MonthExtentHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonthExtentHeightProperty =
            DependencyProperty.Register("MonthExtentHeight", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion MonthExtentHeight

        #region MonthExtentWidth

        public double MonthExtentWidth
        {
            get { return (double)GetValue(MonthExtentWidthProperty); }
            private set { SetValue(MonthExtentWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MonthExtentWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonthExtentWidthProperty =
            DependencyProperty.Register("MonthExtentWidth", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion MonthExtentWidth

        #region DayExtentHeight

        public double DayExtentHeight
        {
            get { return (double)GetValue(DayExtentHeightProperty); }
            private set { SetValue(DayExtentHeightProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DayExtentHeight.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DayExtentHeightProperty =
            DependencyProperty.Register("DayExtentHeight", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion DayExtentHeight

        #region DayExtentWidth

        public double DayExtentWidth
        {
            get { return (double)GetValue(DayExtentWidthProperty); }
            private set { SetValue(DayExtentWidthProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DayExtentWidth.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DayExtentWidthProperty =
            DependencyProperty.Register("DayExtentWidth", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0)));

        #endregion DayExtentWidth

        #region YearOffsetX

        public double YearOffsetX
        {
            get { return (double)GetValue(YearOffsetXProperty); }
            set { SetValue(YearOffsetXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for YearOffsetX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YearOffsetXProperty =
            DependencyProperty.Register("YearOffsetX", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(YearOffsetXChanged)));

        private static void YearOffsetXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._YearItemsPart == null) return;
            fds._YearItemsPart.RenderTransform = new TranslateTransform() { X = fds.YearOffsetX, Y = fds.YearOffsetY };
        }

        #endregion YearOffsetX

        #region YearOffsetY

        public double YearOffsetY
        {
            get { return (double)GetValue(YearOffsetYProperty); }
            set { SetValue(YearOffsetYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for YearOffsetY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty YearOffsetYProperty =
            DependencyProperty.Register("YearOffsetY", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(YearOffsetYChanged)));

        private static void YearOffsetYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._YearItemsPart == null) return;
            fds._YearItemsPart.RenderTransform = new TranslateTransform() { X = fds.YearOffsetX, Y = fds.YearOffsetY };
        }

        #endregion YearOffsetY

        #region MonthOffsetX

        public double MonthOffsetX
        {
            get { return (double)GetValue(MonthOffsetXProperty); }
            set { SetValue(MonthOffsetXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MonthOffsetX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonthOffsetXProperty =
            DependencyProperty.Register("MonthOffsetX", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(MonthOffsetXChanged)));

        private static void MonthOffsetXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._MonthItemsPart == null) return;
            fds._MonthItemsPart.RenderTransform = new TranslateTransform() { X = fds.MonthOffsetX, Y = fds.MonthOffsetY };
        }

        #endregion MonthOffsetX

        #region MonthOffsetY

        public double MonthOffsetY
        {
            get { return (double)GetValue(MonthOffsetYProperty); }
            set { SetValue(MonthOffsetYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for MonthOffsetY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MonthOffsetYProperty =
            DependencyProperty.Register("MonthOffsetY", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(MonthOffsetYChanged)));

        private static void MonthOffsetYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._MonthItemsPart == null) return;
            fds._MonthItemsPart.RenderTransform = new TranslateTransform() { X = fds.MonthOffsetX, Y = fds.MonthOffsetY };
        }

        #endregion MonthOffsetY

        #region DayOffsetX

        public double DayOffsetX
        {
            get { return (double)GetValue(DayOffsetXProperty); }
            set { SetValue(DayOffsetXProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DayOffsetX.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DayOffsetXProperty =
            DependencyProperty.Register("DayOffsetX", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(DayOffsetXChanged)));

        private static void DayOffsetXChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._DayItemsPart == null) return;
            fds._DayItemsPart.RenderTransform = new TranslateTransform() { X = fds.DayOffsetX, Y = fds.DayOffsetY };
        }

        #endregion DayOffsetX

        #region DayOffsetY

        public double DayOffsetY
        {
            get { return (double)GetValue(DayOffsetYProperty); }
            set { SetValue(DayOffsetYProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DayOffsetY.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DayOffsetYProperty =
            DependencyProperty.Register("DayOffsetY", typeof(double), typeof(FlowDateSelector), new PropertyMetadata(Convert.ToDouble(0), new PropertyChangedCallback(DayOffsetYChanged)));

        private static void DayOffsetYChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            FlowDateSelector fds = d as FlowDateSelector;
            if (fds == null || fds._DayItemsPart == null) return;
            fds._DayItemsPart.RenderTransform = new TranslateTransform() { X = fds.DayOffsetX, Y = fds.DayOffsetY };
        }

        #endregion DayOffsetY

        #endregion Dependancy Properties

        #region Override Methods

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();
            GetPartsFromTemplate();
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            Size s = base.ArrangeOverride(finalSize);
            
            //
            // Set offsets and calculate extents
            //
            CalculateDateExtents();
            SetDayOffsetFromCurrentDate();
            SetMonthOffsetFromCurrentDate();
            SetYearOffsetFromCurrentDate();

            return s;
        }

        #endregion Override Methods

        #region Private Methods

        /// <summary>
        /// Get the required parts from the template
        /// </summary>
        private void GetPartsFromTemplate()
        {
            //
            // Unsubscribe from old events of any old parts
            //
            UnsubscribeFromPartEvents();

            //
            // Get new controls from template
            //
            _YearItemsPart = GetTemplateChild("PART_YearsItems") as ItemsControl;
            _MonthItemsPart = GetTemplateChild("PART_MonthsItems") as ItemsControl;
            _DayItemsPart = GetTemplateChild("PART_DaysItems") as ItemsControl;

            _YearViewPortPart = GetTemplateChild("PART_YearsViewPort") as Canvas;
            _MonthViewPortPart = GetTemplateChild("PART_MonthsViewPort") as Canvas;
            _DayViewPortPart = GetTemplateChild("PART_DaysViewPort") as Canvas;

            //
            // Subscribe to part events
            //
            SubscribeToPartEvents();

            //
            // Ensure parts are populated
            //
            PopulateDateIncrements();
        }

        /// <summary>
        /// Unsubscribes from any events that might be subscribe to on the parts
        /// </summary>
        private void UnsubscribeFromPartEvents()
        {
            if (_YearItemsPart != null)
            {
                _YearItemsPart.MouseLeftButtonDown -= new MouseButtonEventHandler(_YearItems_MouseLeftButtonDown);
                _YearItemsPart.MouseLeftButtonUp -= new MouseButtonEventHandler(_YearItems_MouseLeftButtonUp);
            }

            if (_MonthItemsPart != null)
            {
                _MonthItemsPart.MouseLeftButtonDown -= new MouseButtonEventHandler(_MonthItems_MouseLeftButtonDown);
                _MonthItemsPart.MouseLeftButtonUp -= new MouseButtonEventHandler(_MonthItems_MouseLeftButtonUp);
            }

            if (_DayItemsPart != null)
            {
                _DayItemsPart.MouseLeftButtonDown -= new MouseButtonEventHandler(_DayItems_MouseLeftButtonDown);
                _DayItemsPart.MouseLeftButtonUp -= new MouseButtonEventHandler(_DayItems_MouseLeftButtonUp);
            }
        }

        /// <summary>
        /// Subscribe to the needed events on the parts
        /// </summary>
        private void SubscribeToPartEvents()
        {
            _YearItemsPart.MouseLeftButtonDown += new MouseButtonEventHandler(_YearItems_MouseLeftButtonDown);
            _YearItemsPart.MouseLeftButtonUp += new MouseButtonEventHandler(_YearItems_MouseLeftButtonUp);
            _YearItemsPart.MouseMove += new MouseEventHandler(_YearItemsPart_MouseMove);
            _YearItemsPart.LostMouseCapture += new MouseEventHandler(_YearItemsPart_LostMouseCapture);

            _MonthItemsPart.MouseLeftButtonDown += new MouseButtonEventHandler(_MonthItems_MouseLeftButtonDown);
            _MonthItemsPart.MouseLeftButtonUp += new MouseButtonEventHandler(_MonthItems_MouseLeftButtonUp);
            _MonthItemsPart.MouseMove += new MouseEventHandler(_MonthItemsPart_MouseMove);
            _MonthItemsPart.LostMouseCapture += new MouseEventHandler(_MonthItemsPart_LostMouseCapture);

            _DayItemsPart.MouseLeftButtonDown += new MouseButtonEventHandler(_DayItems_MouseLeftButtonDown);
            _DayItemsPart.MouseLeftButtonUp += new MouseButtonEventHandler(_DayItems_MouseLeftButtonUp);
            _DayItemsPart.MouseMove += new MouseEventHandler(_DayItemsPart_MouseMove);
            _DayItemsPart.LostMouseCapture += new MouseEventHandler(_DayItemsPart_LostMouseCapture);

            _YearViewPortPart.SizeChanged += new SizeChangedEventHandler(_YearViewPortPart_SizeChanged);
            _MonthViewPortPart.SizeChanged += new SizeChangedEventHandler(_MonthViewPortPart_SizeChanged);
            _DayViewPortPart.SizeChanged += new SizeChangedEventHandler(_DayViewPortPart_SizeChanged);
        }

        /// <summary>
        /// Measures the ItemsControls and calculates the Year, Month & Day extents from the current parts
        /// </summary>
        private void CalculateDateExtents()
        {
            if (_YearItemsPart != null)
            {
                foreach (DateIncrement item in _Years.Values)
                {
                    FrameworkElement itemContainer = _YearItemsPart.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
                    item.TopLeft = GetPositionOfFrameWorkElement(itemContainer, _YearItemsPart);
                    item.BottomRight = new Point(item.TopLeft.X + itemContainer.ActualWidth, item.TopLeft.Y + itemContainer.ActualHeight);
                }

                FrameworkElement lastItemContainer = _YearItemsPart.ItemContainerGenerator.ContainerFromIndex(_YearItemsPart.Items.Count - 1) as FrameworkElement;
                Point position = GetPositionOfFrameWorkElement(lastItemContainer, _YearItemsPart);
                position.X += lastItemContainer.ActualWidth;
                position.Y += lastItemContainer.ActualHeight;

                YearExtentHeight = position.Y;
                YearExtentWidth = position.X;
            }
            else
            {
                YearExtentHeight = 0;
                YearExtentWidth = 0;
            }

            if (_MonthItemsPart != null)
            {
                foreach (DateIncrement item in _Months.Values)
                {
                    FrameworkElement itemContainer = _MonthItemsPart.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
                    item.TopLeft = GetPositionOfFrameWorkElement(itemContainer, _MonthItemsPart);
                    item.BottomRight = new Point(item.TopLeft.X + itemContainer.ActualWidth, item.TopLeft.Y + itemContainer.ActualHeight);
                }

                FrameworkElement lastItemContainer = _MonthItemsPart.ItemContainerGenerator.ContainerFromIndex(_MonthItemsPart.Items.Count - 1) as FrameworkElement;
                Point position = GetPositionOfFrameWorkElement(lastItemContainer, _MonthItemsPart);
                position.X += lastItemContainer.ActualWidth;
                position.Y += lastItemContainer.ActualHeight;

                MonthExtentHeight = position.Y;
                MonthExtentWidth = position.X;
            }
            else
            {
                MonthExtentHeight = 0;
                MonthExtentWidth = 0;
            }

            if (_DayItemsPart != null)
            {
                foreach (DateIncrement item in _Days.Values)
                {
                    FrameworkElement itemContainer = _DayItemsPart.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
                    item.TopLeft = GetPositionOfFrameWorkElement(itemContainer, _DayItemsPart);
                    item.BottomRight = new Point(item.TopLeft.X + itemContainer.ActualWidth, item.TopLeft.Y + itemContainer.ActualHeight);
                }

                FrameworkElement lastItemContainer = _DayItemsPart.ItemContainerGenerator.ContainerFromIndex(_DayItemsPart.Items.Count - 1) as FrameworkElement;
                Point position = GetPositionOfFrameWorkElement(lastItemContainer, _DayItemsPart);
                position.X += lastItemContainer.ActualWidth;
                position.Y += lastItemContainer.ActualHeight;

                DayExtentHeight = position.Y;
                DayExtentWidth = position.X;
            }
            else
            {
                DayExtentHeight = 0;
                DayExtentWidth = 0;
            }
        }
        
        /// <summary>
        /// Populates the date items controls
        /// </summary>
        private void PopulateDateIncrements()
        {
            _Years = new Dictionary<string, DateIncrement>();
            _Months = new Dictionary<string, DateIncrement>();
            _Days = new Dictionary<string, DateIncrement>();

            DateTime runningDate = new DateTime(StartYear, 1, 1);

            for (int y = 0; y < EndYear - StartYear; y++)
            {
                string YearText = runningDate.ToString(_YearFormat);
                _Years.Add(YearText, new DateIncrement() { Description = YearText, TopLeft = new Point(), BottomRight = new Point(), Date = new DateTime(runningDate.Year, 1, 1) });

                for (int m = 0; m < 12; m++)
                {
                    string MonthText = runningDate.ToString(_MonthFormat);
                    _Months.Add(MonthText, new DateIncrement() { Description = MonthText, TopLeft = new Point(), BottomRight = new Point(), Date = new DateTime(runningDate.Year, runningDate.Month, 1) });

                    for (int d = 0; d < DateTime.DaysInMonth(runningDate.Year, runningDate.Month); d++)
                    {
                        string DayText = runningDate.ToString(_DayFormat);
                        _Days.Add(DayText, new DateIncrement() { Description = DayText, TopLeft = new Point(), BottomRight = new Point(), Date = new DateTime(runningDate.Year, runningDate.Month, runningDate.Day) });

                        runningDate = runningDate.AddDays(1);
                    }
                }
            }

            if (_YearItemsPart != null)
            {
                _YearItemsPart.ItemsSource = _Years.Values;
            }

            if (_MonthItemsPart != null)
            {
                _MonthItemsPart.ItemsSource = _Months.Values;
            }

            if (_DayItemsPart != null)
            {
                _DayItemsPart.ItemsSource = _Days.Values;
            }
        }

        /// <summary>
        /// Gets the position of an element relative to another element
        /// </summary>
        private Point GetPositionOfFrameWorkElement(UIElement element, UIElement relativeTo)
        {
            Point p = new Point();
            
            if (element != null)
            {
                GeneralTransform ft = element.TransformToVisual(relativeTo);
                p = ft.Transform(new Point(0, 0));
            }

            return p;
        }

        /// <summary>
        /// Sets the year offset form the current date
        /// </summary>
        private void SetYearOffsetFromCurrentDate()
        {
            //
            // If mouse is down over years element skip
            //
            if (_MousePressedOnYears) return;

            //
            // Get year DateIncrement for current date
            //
            string YearText = CurrentDate.ToString(_YearFormat);
            
            DateIncrement YearDateIncrement;
            if (_YearViewPortPart != null && _Years.TryGetValue(YearText, out YearDateIncrement))
            {
                double daysInYear = (new DateTime(CurrentDate.Year, 12, 31) - new DateTime(CurrentDate.Year, 1, 1)).Days + 1;

                double newOffsetX = _YearViewPortPart.ActualWidth / 2; // Offset to center of viewport
                newOffsetX -= YearDateIncrement.TopLeft.X; // Offset to item which represents YearDateIncrement
                newOffsetX -= (YearDateIncrement.BottomRight.X - YearDateIncrement.TopLeft.X) / 12 * CurrentDate.Month; // Offset to month relative to item
                newOffsetX -= (YearDateIncrement.BottomRight.X - YearDateIncrement.TopLeft.X) / daysInYear * CurrentDate.Day; // Offset to day relative to item
                YearOffsetX = newOffsetX;

                double newOffsetY = _YearViewPortPart.ActualHeight / 2; // Offset to center of viewport
                newOffsetY -= YearDateIncrement.TopLeft.Y; // Offset to item which represents YearDateIncrement
                newOffsetY -= (YearDateIncrement.BottomRight.Y - YearDateIncrement.TopLeft.Y) / 2; // Offset to center of item
                YearOffsetY = newOffsetY;

                //_YearItemsPart.RenderTransform = new TranslateTransform() { X = YearOffsetX, Y = YearOffsetY };
            }
            else
            {
                YearOffsetX = 0;
                YearOffsetY = 0;
            }
        }

        /// <summary>
        /// Sets the month offset form the current date
        /// </summary>
        private void SetMonthOffsetFromCurrentDate()
        {
            //
            // If mouse is down over years element skip
            //
            if (_MousePressedOnMonths) return;

            //
            // Get year DateIncrement for current date
            //
            string MonthText = CurrentDate.ToString(_MonthFormat);

            DateIncrement MonthDateIncrement;
            if (_MonthViewPortPart != null && _Months.TryGetValue(MonthText, out MonthDateIncrement))
            {
                double daysInMonth = DateTime.DaysInMonth(CurrentDate.Year, CurrentDate.Month);

                double newOffsetX = _MonthViewPortPart.ActualWidth / 2; // Offset to center of viewport
                newOffsetX -= MonthDateIncrement.TopLeft.X; // Offset to item which represents MonthDateIncrement
                newOffsetX -= (MonthDateIncrement.BottomRight.X - MonthDateIncrement.TopLeft.X) / daysInMonth * CurrentDate.Day; // Offset to day relative to item
                MonthOffsetX = newOffsetX;

                double newOffsetY = _MonthViewPortPart.ActualHeight / 2; // Offset to center of viewport
                newOffsetY -= MonthDateIncrement.TopLeft.Y; // Offset to item which represents MonthDateIncrement
                newOffsetY -= (MonthDateIncrement.BottomRight.Y - MonthDateIncrement.TopLeft.Y) / 2; // Offset to center of item
                MonthOffsetY = newOffsetY;

                //_MonthItemsPart.RenderTransform = new TranslateTransform() { X = MonthOffsetX, Y = MonthOffsetY };
            }
            else
            {
                MonthOffsetX = 0;
                MonthOffsetY = 0;
            }
        }

        /// <summary>
        /// Sets the day offset form the current date
        /// </summary>
        private void SetDayOffsetFromCurrentDate()
        {
            //
            // If mouse is down over years element skip
            //
            if (_MousePressedOnDays) return;

            //
            // Get year DateIncrement for current date
            //
            string DayText = CurrentDate.ToString(_DayFormat);

            DateIncrement DayDateIncrement;
            if (_DayViewPortPart != null && _Days.TryGetValue(DayText, out DayDateIncrement))
            {
                double newOffsetX = _DayViewPortPart.ActualWidth / 2; // Offset to center of viewport
                newOffsetX -= DayDateIncrement.TopLeft.X; // Offset to item which represents DayDateIncrement
                newOffsetX -= (DayDateIncrement.BottomRight.X - DayDateIncrement.TopLeft.X) / 2; // Offset to half way through the day
                DayOffsetX = newOffsetX;

                double newOffsetY = _DayViewPortPart.ActualHeight / 2; // Offset to center of viewport
                newOffsetY -= DayDateIncrement.TopLeft.Y; // Offset to item which represents MonthDateIncrement
                newOffsetY -= (DayDateIncrement.BottomRight.Y - DayDateIncrement.TopLeft.Y) / 2; // Offset to center of item
                DayOffsetY = newOffsetY;

                //_DayItemsPart.RenderTransform = new TranslateTransform() { X = DayOffsetX, Y = DayOffsetY };
            }
            else
            {
                DayOffsetX = 0;
                DayOffsetY = 0;
            }
        }

        /// <summary>
        /// Sets the current date from the year offset
        /// </summary>
        private void SetCurentDateFromYearOffset()
        {
            double _YearPartMidPoint = _YearViewPortPart.ActualWidth / 2;
            DateIncrement dateIncrement = _Years.Values.FirstOrDefault(di => Math.Abs(YearOffsetX - _YearPartMidPoint) >= (di.TopLeft.X) && Math.Abs(YearOffsetX - _YearPartMidPoint) <= (di.BottomRight.X));

            if (dateIncrement != null)
            {
                double translatedOffset = Math.Abs(YearOffsetX - _YearPartMidPoint);
                double diff = translatedOffset - dateIncrement.TopLeft.X;
                double daysInYear = (new DateTime(dateIncrement.Date.Year, 12, 31) - new DateTime(dateIncrement.Date.Year, 1, 1)).Days + 1;
                double dayDiff = diff / ((dateIncrement.BottomRight.X - dateIncrement.TopLeft.X) / daysInYear);
                CurrentDate = dateIncrement.Date.AddDays(dayDiff);
            }
        }

        /// <summary>
        /// Sets the current date from the month offset
        /// </summary>
        private void SetCurentDateFromMonthOffset()
        {
            double _MonthPartMidPoint = _MonthViewPortPart.ActualWidth / 2;
            DateIncrement dateIncrement = _Months.Values.FirstOrDefault(di => Math.Abs(MonthOffsetX - _MonthPartMidPoint) >= (di.TopLeft.X) && Math.Abs(MonthOffsetX - _MonthPartMidPoint) <= (di.BottomRight.X));

            if (dateIncrement != null)
            {
                double translatedOffset = Math.Abs(MonthOffsetX - _MonthPartMidPoint);
                double diff = translatedOffset - dateIncrement.TopLeft.X;
                double daysInMonth = DateTime.DaysInMonth(dateIncrement.Date.Year, dateIncrement.Date.Month);
                double dayDiff = diff / ((dateIncrement.BottomRight.X - dateIncrement.TopLeft.X) / daysInMonth);
                CurrentDate = dateIncrement.Date.AddDays(dayDiff);
            }
        }

        /// <summary>
        /// Sets the current date from the day offset
        /// </summary>
        private void SetCurentDateFromDayOffset()
        {
            double _DayPartMidPoint = _DayViewPortPart.ActualWidth / 2;
            DateIncrement dateIncrement = _Days.Values.FirstOrDefault(di => Math.Abs(DayOffsetX - _DayPartMidPoint) >= (di.TopLeft.X) && Math.Abs(DayOffsetX - _DayPartMidPoint) <= (di.BottomRight.X));

            if (dateIncrement != null)
            {
                double translatedOffset = Math.Abs(MonthOffsetX - _DayPartMidPoint);
                double diff = translatedOffset - dateIncrement.TopLeft.X;
                //double daysInMonth = DateTime.DaysInMonth(dateIncrement.Date.Year, dateIncrement.Date.Month);
                //double dayDiff = diff / ((dateIncrement.BottomRight.X - dateIncrement.TopLeft.X) / daysInMonth);
                CurrentDate = new DateTime(dateIncrement.Date.Year, dateIncrement.Date.Month, dateIncrement.Date.Day);
            }
        }

        #endregion Private Methods

        #region Parts Events Handlers

        private void _DayItems_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _DayItemsPart.ReleaseMouseCapture();
        }

        private void _DayItems_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = true;

            //
            // Record pressed point
            //
            _LastPoint = e.GetPosition(this);

            _DayItemsPart.CaptureMouse();
        }

        private void _DayItemsPart_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_MousePressedOnDays) return;

            Point p = e.GetPosition(this);

            double deltaX = p.X - _LastPoint.X;
            //double deltaY = p.Y - _LastPoint.Y;

            DayOffsetX += deltaX;
            //DayOffsetY += deltaY;

            _LastPoint = p;

            SetCurentDateFromDayOffset();
        }

        private void _DayItemsPart_LostMouseCapture(object sender, MouseEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = false;
        }

        private void _MonthItems_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _MonthItemsPart.ReleaseMouseCapture();
        }

        private void _MonthItems_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = true;
            _MousePressedOnDays = false;

            //
            // Record pressed point
            //
            _LastPoint = e.GetPosition(this);

            _MonthItemsPart.CaptureMouse();
        }

        private void _MonthItemsPart_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_MousePressedOnMonths) return;

            Point p = e.GetPosition(this);

            double deltaX = p.X - _LastPoint.X;
            //double deltaY = p.Y - _LastPoint.Y;

            MonthOffsetX += deltaX;
            //MonthOffsetY += deltaY;

            _LastPoint = p;

            SetCurentDateFromMonthOffset();
        }

        private void _MonthItemsPart_LostMouseCapture(object sender, MouseEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = false;
        }

        private void _YearItems_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            _YearItemsPart.ReleaseMouseCapture();
        }

        private void _YearItems_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = true;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = false;

            //
            // Record pressed point
            //
            _LastPoint = e.GetPosition(this);

            _YearItemsPart.CaptureMouse();
        }

        private void _YearItemsPart_MouseMove(object sender, MouseEventArgs e)
        {
            if (!_MousePressedOnYears) return;

            Point p = e.GetPosition(this);
            
            double deltaX = p.X - _LastPoint.X;
            //double deltaY = p.Y - _LastPoint.Y;
            
            YearOffsetX += deltaX;
            //YearOffsetY += deltaY;

            _LastPoint = p;

            SetCurentDateFromYearOffset();

        }

        private void _YearItemsPart_LostMouseCapture(object sender, MouseEventArgs e)
        {
            //
            // Set mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = false;
        }

        private void _DayViewPortPart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //
            // Set canvas clip to the size of the canvas
            //
            _DayViewPortPart.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, _DayViewPortPart.ActualWidth, _DayViewPortPart.ActualHeight) };
        }

        private void _MonthViewPortPart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //
            // Set canvas clip to the size of the canvas
            //
            _MonthViewPortPart.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, _MonthViewPortPart.ActualWidth, _MonthViewPortPart.ActualHeight) };
        }

        private void _YearViewPortPart_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            //
            // Set canvas clip to the size of the canvas
            //
            _YearViewPortPart.Clip = new RectangleGeometry() { Rect = new Rect(0, 0, _YearViewPortPart.ActualWidth, _YearViewPortPart.ActualHeight) };
        }

        #endregion Parts Events Handlers

        #region Control Event Handlers

        private void FlowDateSelector_LostMouseCapture(object sender, MouseEventArgs e)
        {
            //
            // Reset mouse pressed statuses
            //
            _MousePressedOnYears = false;
            _MousePressedOnMonths = false;
            _MousePressedOnDays = false;
        }

        #endregion Control Event Handlers
    }
}
